package org.testis.gui.mvc.controller.testdesign;

import org.testis.gui.mvc.controller.*;
import org.testis.gui.mvc.model.testdesign.DataObjectModel;
import org.testis.gui.mvc.utilities.ParameterTableModel;
import org.testis.gui.mvc.view.testdesign.DataObjectView;
import org.testis.orm.labelhandler.LabelHandlerFactory;
import org.testis.testdriver.AtofEnumaration;
import org.testis.utils.tree.TreeNode;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import org.orm.PersistentException;
import org.testis.orm.classes.T_test_dataobject_result_types;
import org.testis.orm.refinedclasses.*;
import org.testis.orm.refinedclasses.TestDesignTreeRefined.TestDesignTreeParameters;
import org.testis.orm.testdesign.TestPartSaver;
import org.testis.utils.middleware.TestDesign;
import org.testis.utils.parametermodel.ParameterModel;
import org.testis.utils.tree.*;

/**
 *
 * @author Cahit Emir Erel
 */
public class DataObjectController extends AbstractController implements SaveAble, Addable {

    public static final String CHANGE_MODUL_TYPE = "ModulType";
    public static final String CHANGE_KEY = "Key";
    public static final String CHANGE_NAME = "Name";
    public static final String CLEAR_TABLE_SELECTION = "ClearTable";
    public static final String CHANGE_EXPECTED_RESULT = "ExpectedResult";
    public static final String CHANGE_VALUE = "Value";
    public static final String ADD_KEY = "AddKeyToList";
    public static final String EDITABLE_KEY = "EditableKey";
    public static final String REMOVE_SELECTED_KEY = "RemoveSelectedKeyFromList";
    public static final String REMOVE_ALL_KEYS = "RemoveAllKeysFromList";
    public static final String SHOW_ADD_DATA_OBJECT = "DataObjectVisibility";
    public static final String CHANGE_LAST_WARNING = "LastWarning";
    private TestDesignTreeNode m_selectedNode = null;

    public DataObjectController() {
        super();
    }

    public void operationCloseWindow() {
        cleanAll();
    }

    public String[] operationGetKeys(String type,
            boolean editMode) {
        String[] result = null;
        TestDesignTreeNode parentNode = operationGetSelectedNode();
        String testStepActionName = TestDesign.getTestStepActionName(
                parentNode.getReference());
        if (type.equals("WebAutomationModule") && !(testStepActionName.equals(
                "HandleDutIp")) && !(testStepActionName.equals(
                "DefaultValueVerification")) && !(testStepActionName.equals(
                "LanguageSelection")) && !(testStepActionName.equals(
                "PageContentVerification"))) {
            operationLoadDynamicMap();
            TestDesignControllerList.newInstance().getTreeController().
                    operationShowTab(1);
            TestDesignControllerList.newInstance().getTreeController().
                    operationHideTab(0);
            operationChangeEditableKey(true);
        } else {
            result = TestDesign.getTestStepKeys(parentNode.getReference());
            operationChangeEditableKey(false);
        }
        return result;
    }

    public String[] operationGetExpectedResultTypesList() {
        return TestDesign.newInstance().getExpectedResultTypesList();
    }

    public void operationNewDataObject(String modulType) {
        initialize(false);

        TestDesignControllerList.newInstance().getLabelPairSettingsController().operationNewSettings();

        setModelProperty(CHANGE_MODUL_TYPE, modulType);
        m_selectedNode = TestDesignControllerList.newInstance().getTreeController().getSelectedTreeNode();
        setModelProperty(SHOW_ADD_DATA_OBJECT, true);
    }

    public void operationChangeEditableKey(Boolean editable) {
        setModelProperty(EDITABLE_KEY, editable);
    }

    public Boolean operationGetEditableKey() {
        return (Boolean) getModelProperty(EDITABLE_KEY);
    }

    public void initialize(boolean editMode) {
        ParameterTableModel temp = new ParameterTableModel();
        super.addModel(new DataObjectModel(temp));
        DataObjectView dataObjectView = new DataObjectView(TestDesignControllerList.newInstance().
                getParentFrame(),
                false,
                editMode,
                temp);
        super.addView(dataObjectView);
        dataObjectView.pack();
    }

    public void operationSaveDataObject(String name,
            ParameterTableModel parameterTableModel,
            String expectedResultType, LinkedList<String> labels) {
        HashMap<String, Object> params = new HashMap<String, Object>();
        params.put("name", name);
        params.put("parameterTableModel", parameterTableModel);
        params.put("expectedResultType", expectedResultType);
        params.put("labels", labels);

        LinkedHashMap<Integer, String> listOfCond = ConditionsController.getInstance().getConditionsList();

        if (listOfCond != null) {
            params.put("conditions", listOfCond);
        }

        TestDesignControllerList.newInstance().getTreeController().
                operationHandleSaveClones(this,
                m_selectedNode,
                params);
    }

    @Override
    public boolean operationSave(HashMap<String, Object> params,
            TreeNode node,
            boolean dynamicSaveOnly) {

        if (dynamicSaveOnly) {
            params.put("dynamicSaveOnly", "true");
        }
        return TestPartSaver.operationSave(params,
                (TestDesignTreeNode) node);

    }

    public void operationAddDataObject(String name,
            ParameterTableModel parameterTableModel,
            String modulType,
            String expectedResultType,
            LinkedList<String> labels) throws PersistentException {
        TestDesignTreeNode node = TestDesignControllerList.newInstance().
                getTreeController().getSelectedTreeNode();
        HashMap<String, Object> params = new HashMap<String, Object>();
        params.put("name", name);
        params.put("parameterTableModel", parameterTableModel);
        params.put("modulType", modulType);
        params.put("expectedResultType", expectedResultType);
        params.put("labels", labels);

        LinkedHashMap<Integer, String> listOfCond = ConditionsController.getInstance().getConditionsList();

        if (listOfCond != null) {
            params.put("conditions", listOfCond);
        }
        TestDesignControllerList.newInstance().getTreeController().operationHandleAdd(
                this, node, params);
    }

    public void operationEditDataObject(TestDesignTreeNode node) {
        if (node.getReference().getType() == null) {
            System.out.println("Not found reference type for DataObject");
            return;
        } else if (node.getReference().getType().getName() == null) {
            System.out.println(
                    "Not found reference type name for DataObject");
            return;
        }
        if (node.getReference().getType().getName().equals("DataObject")) {
            DataObjectRefined designpart = null;
            designpart = (DataObjectRefined) node.getObject();
            initialize(true);
            if (designpart == null) {
                System.out.println("Not found design part for DataObject");
                return;
            }
            String moduleType = TestDesign.getModuleTypeName(
                    designpart.getReference().getModulTypeId());

            setModelProperty(CHANGE_MODUL_TYPE, moduleType);
            setModelProperty(CHANGE_NAME,
                    designpart.getName().toString());
            LinkedHashMap<String, String> parameters =
                    designpart.getKeyValueHashMap();
            String[] keys = new String[parameters.keySet().size()];
            keys = parameters.keySet().toArray(keys);
            for (int i = 0; i < keys.length; i++) {
                ParameterModel param =
                        new ParameterModel(keys[i],
                        parameters.get(keys[i]));
                setModelProperty(ADD_KEY, param);
            }

            TestDesignControllerList.newInstance().getLabelPairSettingsController().operationEditSettings(
                    node);

            Integer expectedResultType = node.getReference().getParameterIntValue(
                    TestDesignTreeParameters.ExpectedResultTypeId.name());
            if (expectedResultType != null) {
                T_test_dataobject_result_types type = TestDesign.getExpectedResultTypeById(
                        expectedResultType);
                if (isValid(type,
                        "expected result")) {
                    setModelProperty(CHANGE_EXPECTED_RESULT,
                            type.getName());
                }
            }
            m_selectedNode = node;
            setModelProperty(SHOW_ADD_DATA_OBJECT, true);

        }
    }

    public void operationLoadDynamicMap() {
        /*  TestDesignTreeNode parentNode = operationGetSelectedNode();
        if (parentNode.getDevice() == null) {
        while (!(parentNode.getObject() instanceof TestPlanRefined)) {
        parentNode = parentNode.getParent();
        }
        }
        DynamicMapControllerList.newInstance().getDynamicMapTreeController().
        operationLoadTreeFromDatabase(parentNode.getDevice().
        getActiveFirmware().getName(),
        parentNode.getDevice().
        getName());*/
    }

    public TestDesignTreeNode operationGetSelectedNode() {
        return TestDesignControllerList.newInstance().getTreeController().
                getSelectedTreeNode();
    }

    public boolean isValid(Object obj,
            String errMsg) {
        if (obj != null) {
            return true;
        } else {
            System.out.println("Not found " + errMsg + " for data object");
        }
        return false;
    }

    public DesignPart operationGetSelectedDesignPart() {
        return TestDesignControllerList.newInstance().getTreeController().
                operationGetSelectedDesignPart();
    }

    public void operationChangeName(String name) {
        setModelProperty(CHANGE_NAME,
                name);
    }

    public void operationChangeKey(String key,
            boolean editable) {
        operationChangeKeySelection(key);
        if (editable) {
            operationChangeValue("");
        }
        operationClearTable();
    }

    public void operationChangeKeySelection(String key) {
        setModelProperty(CHANGE_KEY,
                key);
    }

    public void operationClearTable() {
        setModelProperty(CLEAR_TABLE_SELECTION,
                true);
    }

    public String operationGetValue() {
        return (String) getModelProperty(CHANGE_VALUE);
    }

    public void operationChangeValue(String value) {
        setModelProperty(CHANGE_VALUE,
                value);
    }

    public void operationChangeExpectedResult(String ExpectedResult) {
        setModelProperty(CHANGE_EXPECTED_RESULT,
                ExpectedResult);
    }

    public void operationModulType(String modulType) {
        setModelProperty(CHANGE_MODUL_TYPE,
                modulType);
    }

    public void operationRemoveAllKeys(boolean editable) {
        setModelProperty(REMOVE_ALL_KEYS,
                true);
        resetFields(editable);
    }

    public void operationSelectKey(String key,
            String value) {
        operationChangeKeySelection(key);
        operationChangeValue(value);
    }

    public void operationAddKey(boolean editable) {
        String key = (String) getModelProperty(CHANGE_KEY);
        if (key != null && !key.equals("")) {
            setModelProperty(ADD_KEY, true);
            resetFields(editable);
        }
    }

    public void operationAddKey(String keyName) {
        setModelProperty(ADD_KEY, keyName);
    }

    public void operationRemoveKey(Integer selectedKey,
            boolean editable) {
        setModelProperty(REMOVE_SELECTED_KEY, selectedKey);
        resetFields(editable);
    }

    private void resetFields(boolean editable) {
        if (editable) {
            operationSelectKey("", "");
        } else {
            operationChangeValue("");
        }
    }

    private boolean checkIfDataAppliesToCaseDataCountsCorrect(
            TestDesignTreeNode currentTestStep,
            boolean isEditing) {
        TestDesignTreeNode currentTestCase = currentTestStep.getParent();
        List<TestDesignTreeNode> currentTestStepList = new ArrayList();
        TestDesignTreeNode[] currentList =
                currentTestCase.getChildren();
        for (int i = 0; i < currentList.length; i++) {
            TestDesignTreeNode testDesignTreeNode = currentList[i];
            if (testDesignTreeNode.getObject() instanceof TestStepRefined) {
                currentTestStepList.add(testDesignTreeNode);
            }
        }
        TestDesignTreeNode[] currentTestSteps =
                new TestDesignTreeNode[currentTestStepList.size()];
        for (int i = 0; i < currentTestSteps.length; i++) {
            currentTestSteps[i] = currentTestStepList.get(i);
        }
        StringBuffer statusOfAllNodes = new StringBuffer();
        int maxCount = 0;
        for (int i = 0; i < currentTestSteps.length; i++) {
            TestDesignTreeNode testDesignTreeNode = currentTestSteps[i];
            Boolean curDataArray = testDesignTreeNode.getReference().getParameterBooleanValue(
                    TestDesignTreeParameters.DataArrayAppliestoCase.name());
            if (curDataArray == null) {
                curDataArray = false;
            }
            if (curDataArray && testDesignTreeNode.getReference().
                    getId()
                    != currentTestStep.getReference().getId()) {
                if (testDesignTreeNode.getChildren().length > maxCount) {
                    maxCount =
                            testDesignTreeNode.getChildren().length;
                }
                statusOfAllNodes.append(" Step Name:" + testDesignTreeNode.getName() + "  Data Count: "
                        + testDesignTreeNode.getChildren().length
                        + "\n");
            }
        }
        int currentCount = currentTestStep.getChildren().length;
        if (!isEditing) {
            currentCount++;
        }
        if (currentCount != 1 && currentCount != maxCount & maxCount != 0) {
            setModelProperty(CHANGE_LAST_WARNING,
                    "This TestStep has multiple DataObjects and it is marked as DataArrayAppliesToCase, "
                    + "\ntherefore its sibling TestSteps which are also marked as DataArrayAppliesToCase must "
                    + "\nhave the same number of DataObjects or only 1 DataObject attached to it."
                    + "\nCount must be " + maxCount
                    + " at most and 1 at least.\n Current Condition: \n "
                    + statusOfAllNodes.toString());

        }
        return true;
    }

    private boolean checkIfAsyncAndNotDataAppliesToCaseForUniqueData(
            TestDesignTreeNode currentTestStep,
            boolean isEditing) {
        boolean result = true;
        Boolean isAsynch = currentTestStep.getReference().getParameterBooleanValue(
                TestDesignTreeParameters.AsynchCall.name());
        if (isAsynch == null) {
            isAsynch = false;
        }
        Boolean isDataArrayAppliesToCase = currentTestStep.getReference().getParameterBooleanValue(
                TestDesignTreeParameters.DataArrayAppliestoCase.name());
        if (isDataArrayAppliesToCase == null) {
            isDataArrayAppliesToCase = false;
        }

        int childCount = currentTestStep.getChildren().length;
        if (childCount <= 0) {
            result = true;
        } else if (isAsynch && !isDataArrayAppliesToCase && !isEditing) {
            TestStepRefined refined = (TestStepRefined) currentTestStep.getObject();
            String moduleType = TestDesign.getModuleTypeName(
                    refined.getReference().getModulTypeId());
            if (moduleType.equals(
                    AtofEnumaration.AtofModule.WebAutomationModule.name())) {
                setModelProperty(CHANGE_LAST_WARNING,
                        "You can only add one teststepdata to this step...");

                result = false;
            }
        }
        return result;
    }

    public boolean checkConstraints() {
        boolean result = true;
        boolean isEditing = false;
        TestDesignTreeNode selectedNode = TestDesignControllerList.newInstance().
                getTreeController().getSelectedTreeNode();
        TestDesignTreeNode currentTestStep = null;

        if (selectedNode.getObject() instanceof TestStepRefined) {
            //adding test step dataobject
            currentTestStep = selectedNode;
        } else if (selectedNode.getObject() instanceof DataObjectRefined) {
            //editing test data
            currentTestStep = selectedNode.getParent();
            isEditing = true;
        }
        result = checkIfAsyncAndNotDataAppliesToCaseForUniqueData(
                currentTestStep,
                isEditing);
        Boolean isDataArrayAppliesToCase = currentTestStep.getReference().getParameterBooleanValue(
                TestDesignTreeParameters.DataArrayAppliestoCase.name());
        if (isDataArrayAppliesToCase == null) {
            isDataArrayAppliesToCase = false;
        }
        if (isDataArrayAppliesToCase) {//check issue 2638 &3968

            //find list of teststep that isDataArrayAppliesToCase and their dataobject count
            result = checkIfDataAppliesToCaseDataCountsCorrect(currentTestStep,
                    isEditing);
        }


        return result;
    }

    @Override
    public TestDesignTreeNode operationAddObject(HashMap<String, Object> params, TreeNode node, int childIndex) {

        TestDesignTreeNode nodeToAdd = TestPartSaver.operationAdd(params,
                (TestDesignTreeNode) node, childIndex, "DataObject");
        return nodeToAdd;
    }

    @Override
    public boolean isValidSave(HashMap<String, Object> params, TreeNode node) {
        return TestDesign.newInstance().isValidDataObjectToSave(params,
                (TestDesignTreeNode) node);
    }

    @Override
    public boolean isValidAdd(HashMap<String, Object> params, TreeNode node) {
        return TestDesign.newInstance().isValidDataObjectToAdd(params,
                (TestDesignTreeNode) node);
    }

}
